package nb.diskfilesystem;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;


/**
 * 操作用文件模拟出来的磁盘
 */
/*RandomAccessFile raf = new RandomAccessFile("data.txt", "rw"),new时打开流，但又怕这个类的函数里会有很多
没用的对象留下，所以可以一进程序就打开这个文件，结束时关掉流。后面可以改写成这样*/

public class DiskManage {
    private static byte[] fileTable;   //存储文件所有字节
    private static byte[] fatTable;

    // 磁盘初始化：先128个块全部清空，然后给FAT表的前三个字节填上255，然后给盘块2根目录的8个目录项都填上"$",表示这些是空目录项
    public static void start()
    {
        byte[] buffer = new byte[64];
        for (int i = 0; i < 64; i++)
        {
            buffer[i] = 0;
        }
        byte[] buffer1 = readBlock(0);   //buffer1读取第0块的64个字节

        if (buffer1[0] != 0){
            System.out.println("磁盘已初始化");
        }else {
            System.out.println("磁盘未初始化，现在开始初始化");
            //先把前128个字节初始化为0
            for (int i = 0; i < 128; i++) {
                writeBlock(i, buffer);
            }
            //接着把前3个字节初始化为255
            writeOnlyByte(0, 0, 0, (byte) 255);
            writeOnlyByte(0, 0, 1, (byte) 255);
            writeOnlyByte(0, 0, 2, (byte) 255);

            for (int i = 1; i < 8; i++) {
                DiskManage.writeOnlyByte(2, i, 0, (byte) '$');//$表示目录为空·
            }
            DiskManage.writeOnlyByte(2, 0, 0, (byte) 'A');
            DiskManage.writeOnlyByte(2, 0, 5, (byte) 8);
            DiskManage.writeOnlyByte(2, 0, 6, (byte) 3);
            for (int i = 0; i < 8; i++) {
                DiskManage.writeOnlyByte(3, i, 0, (byte) '$');//$表示目录为空·
            }
            //修改fat表
            DiskManage.writeOnlyByte(0, 0, 3, (byte) -1);

            DiskManage.writeOnlyByte(3, 0, 0, (byte) 'B');
            DiskManage.writeOnlyByte(3, 0, 5, (byte) 4);
            DiskManage.writeOnlyByte(3, 0, 6, (byte) 4);
            //修改fat表
            DiskManage.writeOnlyByte(0, 0, 4, (byte) -1);

            DiskManage.writeOnlyByte(3, 1, 0, (byte) 'C');
            DiskManage.writeOnlyByte(3, 1, 1, (byte) 'N');
            DiskManage.writeOnlyByte(3, 1, 2, (byte) 'M');
            DiskManage.writeOnlyByte(3, 1, 5, (byte) 8);
            DiskManage.writeOnlyByte(3, 1, 6, (byte) 5);
            for (int i = 0; i < 8; i++) {
                DiskManage.writeOnlyByte(5, i, 0, (byte) '$');//$表示目录为空·
            }
            //修改fat表
            DiskManage.writeOnlyByte(0, 0, 5, (byte) -1);

        }
        ByteBuffer bufferfat = ByteBuffer.allocate(128);// 创建一个足够容纳两个数组的 ByteBuffer
        bufferfat.put(DiskManage.readBlock(0));// 将第一个数组放入 buffer
        bufferfat.put(DiskManage.readBlock(1));// 将第二个数组放入 buffer
        fatTable=bufferfat.array();// 返回合并后的字节数组

    }

    // 读文件 // 把下标为indexOfSector的盘块的64个字节读到byte[]里返回
    public static byte[] readBlock(int indexOfSector)
    {
        byte[] buffer = new byte[64];
        File file = new File("src/main/data.txt");
        if (!file.exists()) {
            creatDiskFile();
        }else{
            System.out.println("调用readBlock文件已存在");
        }
        try (RandomAccessFile raf = new RandomAccessFile("src/main/data.txt", "rw"))
        {
            long pointer = indexOfSector * 64; //计算从文件开头到目标扇区开始位置的偏移量。因为每个扇区包含64个字节，所以偏移量是扇区索引乘以64。
            for (int i = 0; i < 64; i++)
            {
                raf.seek(pointer + i);  //使用seek方法将文件指针移动到正确的位置，即偏移量加上当前迭代的索引。
                buffer[i] = raf.readByte();
            }
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return buffer;
    }

    public  static  void creatDiskFile(){
        // 创建一个128x64字节数组，总共128*64 = 8192个字节
        byte[] byteArray = new byte[128 * 64];

        // 将其他字节设置为一个整数值，例如：从 0 到 127
        for (int i = 0; i < byteArray.length; i++) {
            byteArray[i] = 0;  // 将每个字节设置为 0 到 127 之间的值
        }

        // 打印一些字节来验证结果
        System.out.println("第一个字节: " + byteArray[0]);  // 应该输出 0
        System.out.println("第二个字节: " + byteArray[1]);  // 应该输出 0
        System.out.println("第三个字节: " + byteArray[2]);  // 应该输出 0
        System.out.println("第十个字节: " + byteArray[9]);  // 应该输出 0
        System.out.println("第128个字节: " + byteArray[127]);  // 应该输出 0
        // 定义输出文件的路径
        String outputFile = "src/main/data.txt";

        // 使用FileOutputStream和BufferedOutputStream写入字节数组到文件
        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) {
            // 将byteArray写入文件
            bufferedOutputStream.write(byteArray);
            // 确保数据被刷新到文件
            bufferedOutputStream.flush();
            System.out.println("字节数组已成功写入文件: " + outputFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 往文件写 // 把byte[]里的64个字节写入到下标为indexOfSector的盘块里
    public static void writeBlock(int indexOfSector, byte[] buffer)
    {
        try (RandomAccessFile raf = new RandomAccessFile("src/main/data.txt", "rw"))//这句new的时候如果没有该文件会自动创建的
        {
            long pointer = indexOfSector * 64;
            for (int i = 0; i < 64; i++)
            {
                raf.seek(pointer + i);
                raf.writeByte(buffer[i]);
            }
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    // 写单个字节,用于写目录项   四个参数：blockNum（块号），listNum（目录项列表号），bNum（字节号），和word（要写入的字节值）。
    public static void writeOnlyByte(int blockNum, int listNum, int bNum, byte word)
    {
        try (RandomAccessFile raf = new RandomAccessFile("src/main/data.txt", "rw"))
        {
            long pointer = blockNum * 64 + listNum * 8 + bNum;
            raf.seek(pointer);
            raf.writeByte(word);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    // 读单个字节，用于读目录项    三个参数：blockNum（块号），listNum（目录项列表号），bNum（字节号）
    public static byte readOnlyByte(int blockNum, int listNum, int bNum)
    {
        byte b = -2;
        try (RandomAccessFile raf = new RandomAccessFile("src/main/data.txt", "rw"))
        {
            long pointer = blockNum * 64 + listNum * 8 + bNum;// 按字节来找的。比如（0，0，3）得到FAT表中的字节3（字节0开始）
            raf.seek(pointer);
            b = raf.readByte();
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return b;
    }


    // 打印模拟硬盘信息，主要用于调试系统 //按字节，打印前8个块
    public static void diskPrint()
    {
        System.out.println("按字节，打印前8个块：");
        byte[] buffer = new byte[64];
        for (int i = 0; i < 8; i++)
        {
            buffer = readBlock(i);
            System.out.print("第" + i + "个块：");
            for (int j = 0; j < 64; j++)
            {
                System.out.printf("%4d", buffer[j]);
            }
            System.out.println();
        }
        System.out.println("---------------------------------------------------");
    }

    public static Directory_Node buildDirectoryTree(int startBlock,Directory_Node parentNode) {
        byte[] buffer = readBlock(startBlock);   //读取这个块的所有字节
        List<Directory_Node> children = new ArrayList<>();
        for (int i = 0; i < 8; i++) {
            byte[] dirEntry = new byte[8];
            System.arraycopy(buffer, i * 8, dirEntry, 0, 8);
            //从 buffer 中拷贝 8 个字节的数据到 dirEntry。每次循环处理一个目录条目，i * 8 确保每次从正确的位置拷贝数据。
            if(readOnlyByte(startBlock,i,0)!=(byte)'$') {
                Directory_Node childNode = new Directory_Node(dirEntry);  //创建子节点实例
                if (childNode.getAttribute() == 8) { // 如果是目录则深搜
                    childNode = buildDirectoryTree(childNode.getStartBlock(), childNode);
                }
                childNode.setParent(parentNode);   //孩子节点给其设置父节点
                children.add(childNode);   //无论是文件还是文件都要加到子列表中
            }
        }
        parentNode.setChildrens(children);   //设置子节点
        return parentNode;
    }

    public static void printDirectoryTree(Directory_Node node, int level) {
        if (node == null) {
            return; // 如果节点为空，直接返回
        }
        // 打印当前节点的信息
        String indent = " ".repeat(level * 2); // 根据层级生成缩进
        String type = (node.getAttribute() == 8) ? "Directory" : "File";
        System.out.println(indent + node.getFile_Name() + " [" + type + "]");

        // 递归打印子节点
        List<Directory_Node> children = node.getChildrens();
        if (children != null) {
            for (Directory_Node child : children) {
                printDirectoryTree(child, level + 1); // 增加缩进级别
            }
        }
    }

}
